/********************************************************************************
*                                                                               *
* kWooka_perferences_utils.cpp -- Some tools for perferences function             *
*                                                                               *
* Copyright (c) Fengren Technology(Guangzhou) Co.LTD. All rights reserved.      *
*                                                                               *
********************************************************************************/


#include "kWooka_perferences.hpp"
#include <wx/file.h>
#include <wx/vector.h>
#ifdef WIN32
#include <windows.h>
#include "enum_process.hpp"
#include <winternl.h>

wxString wxNewGuid() {
    GUID guid;

    if (CoCreateGuid(&guid))
    {
        return wxT("");
    }
    return wxString::Format(wxT("%08X%04X%04x%02X%02X%02X%02X%02X%02X%02X%02X"), guid.Data1, guid.Data2, guid.Data3,
        guid.Data4[0], guid.Data4[1], guid.Data4[2],
        guid.Data4[3], guid.Data4[4], guid.Data4[5],
        guid.Data4[6], guid.Data4[7]);
}

//HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run

typedef LONG(WINAPI* PROCNTQSIP)(HANDLE, UINT, PVOID, ULONG, PULONG);


// PROCNTQSIP NtQueryInformationProcess;

BOOL GetProcessCmdLine(DWORD dwId, LPWSTR wBuf, DWORD dwBufLen);



typedef BOOL(WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
LPFN_ISWOW64PROCESS fnIsWow64Process;
BOOL IsWow64()
{
	BOOL bIsWow64 = FALSE;
	//IsWow64Process is not available on all supported versions of Windows.
	//Use GetModuleHandle to get a handle to the DLL that contains the function
	//and GetProcAddress to get a pointer to the function if available.
	fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(
		GetModuleHandle(TEXT("kernel32")), "IsWow64Process");
	
	if (NULL != fnIsWow64Process)
	{
		if (!fnIsWow64Process(GetCurrentProcess(), &bIsWow64))
		{
			//handle error
		}
	}

	return bIsWow64;
}

BOOL kWookaWindowAddOrDeleteStartup(const bool isadd) {
    //HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
    //RegCreateKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\Microsoft\Windows\CurrentVersion\Run");
    HKEY hKey;

	LONG result = ERROR_SUCCESS;
	if (IsWow64()) {
		result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Run"), 0, KEY_WRITE | KEY_WOW64_32KEY, &hKey);
	}
	else {
		result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Run"), 0, KEY_WRITE, &hKey);
	}
	if (result != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    if (isadd) {
        wxString procName = kWookaGetProcessNameWithPid(0);
		const char* procNameData = (const char*)procName.mb_str();
		//std::string procNameString = procNameData;
		size_t len = procName.Length();
		//size_t len2 = procNameString.length();
		if (IsWow64()) {
			result = RegSetValueExA(hKey, "kPOS", NULL, REG_SZ, (const BYTE*)procNameData, len);
		}
		else {
			result = RegSetValueExA(hKey, "kPOS", NULL, REG_SZ | KEY_WOW64_RES, (const BYTE*)procNameData, len);
		}
		
        if (result != ERROR_SUCCESS)
        {
            return FALSE;
        }
    } else {
        result = RegDeleteValue(hKey, _T("kPOS"));
        if (result != ERROR_SUCCESS)
        {
            return FALSE;
        }
    }
    RegCloseKey(hKey);
    return TRUE;
}
#else
#include <uuid/uuid.h> 

wxString wxNewGuid() {
    uuid_t uu;
    int i;
    uuid_generate(uu);

    return wxString::Format(wxT("%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X"),
        uu[0], uu[1], uu[2], uu[3], uu[4], uu[5], uu[6], uu[7],
        uu[8], uu[9], uu[10], uu[11], uu[12], uu[13], uu[14], uu[15] );
}
#endif

wxConfigBase* m_config;


void kWookaPerferenceInit(bool WXUNUSED(ods)) {
    wxConfigBase* pconfig = new wxConfig(_("kWooca"));
	pconfig->SetRecordDefaults();
	//pconfig->SetAppName(_("kPos-BlockChains POS"));
	pconfig->SetVendorName(_("Fengren Technology (Guangzhou) Co.Ltd"));
    wxConfigBase::Set(pconfig);
}

void kWookaPerferenceUnInit() {
	wxConfigBase* pconfig = wxConfigBase::Set(NULL);
	if (pconfig != NULL) {
		delete pconfig;
	}
}

wxUint32 GetIntPreference(const wxString& bt) {
    wxConfigBase* pconfig = wxConfigBase::Get();
    //wxConfigBase* pconfig = new wxConfig(wxT("kPos-BlockChains POS"));
    long makeTemp = 0;
    pconfig->Read(bt, &makeTemp);
    return (wxUint32) makeTemp;
}

void SetIntPreference(const wxString& bt, wxUint32 newValue) {
    wxConfigBase* pconfig = wxConfigBase::Get();
    // long makeTemp = 0;
    //pconfig->Read(bt, &makeTemp);
    pconfig->Write(bt, newValue);
    pconfig->Flush();
}

wxUint32 GetAndIncreaseIntPreference(const wxString& bt) {
    wxConfigBase* pconfig = wxConfigBase::Get();
    long makeTemp = 0;
    pconfig->Read(bt, &makeTemp);
    wxUint32 newValues = ((wxUint32) makeTemp) + 1;
    pconfig->Write(bt, newValues);
    pconfig->Flush();
    return newValues;
}

wxString kWookaGetPreference(const wxString& bt) {
    wxString val;
    wxConfigBase* pconfig = wxConfigBase::Get();
    pconfig->Read(bt, &val);
    return val;
}

bool kWookaSetPreference(const wxString& bt, const wxString& val) {
    wxConfigBase* pconfig = wxConfigBase::Get();
    pconfig->Write(bt, val);
    pconfig->Flush();
    return true;
}

bool kWookaDeletePreference(const wxString& bt) {
    wxConfigBase* pconfig = wxConfigBase::Get();
    pconfig->DeleteEntry(bt);
    pconfig->Flush();
    return true;
}

void kWookaSetShapeFramePosition(const wxPoint& pt) {
	wxConfigBase* pconfig = wxConfigBase::Get();
	pconfig->Write(wxT("/Perferences/ShapeFrame/X"), pt.x);
	pconfig->Write(wxT("/Perferences/ShapeFrame/Y"), pt.y);
	pconfig->Flush();
}

wxPoint kWookaGetShapeFramePosition() {
	wxPoint pt;
	wxConfigBase* pconfig = wxConfigBase::Get();
	pconfig->Read(wxT("/Perferences/ShapeFrame/X"), &pt.x);
	pconfig->Read(wxT("/Perferences/ShapeFrame/Y"), &pt.y);
	return pt;
}



/**
 Return the short name
 */
wxString kWookaGetPluggedInExecutableFile() {
    wxString plugFile;
    wxConfigBase* pconfig = wxConfigBase::Get();
    pconfig->Read(wxT("/Perferences/PluggedFullname"), &plugFile);
    return plugFile;
}

/**
 Return the full name with path
 */
wxString kWookaGetPluggedInExecutableName() {
    wxString plugFile;
    wxConfigBase* pconfig = wxConfigBase::Get();
    pconfig->Read(wxT("/Perferences/PluggedFilename"), &plugFile);
    return plugFile;
}


void kWookaSetPluggedInExecutableFile(const wxString& fileNameWithPath, const wxString& filename) {
    wxConfigBase* pconfig = wxConfigBase::Get();
    pconfig->Write(wxT("/Perferences/PluggedFullname"), fileNameWithPath);
    if (filename.length() > 0) {
        pconfig->Write(wxT("/Perferences/PluggedFilename"), filename);
    } else {
#ifdef WIN32
        wxString fn = fileNameWithPath.AfterLast('\\');
#else
        wxString fn = fileNameWithPath.AfterLast('/');
#endif
        pconfig->Write(wxT("/Perferences/PluggedFilename"), fn);
    }
    pconfig->Flush();
}

bool kWookaIsEnablePluggedInExecutable() {
    wxInt32  enablePlug = 0;
    wxConfigBase* pconfig = wxConfigBase::Get();
    pconfig->Read(wxT("/Perferences/EnablePlugins"), &enablePlug);
    return enablePlug != 0;
}

void kWookaSetEnablePluggedInExecutable(bool fs) {
    wxInt32  enablePlug = fs ?  1 : 0;
    wxConfigBase* pconfig = wxConfigBase::Get();
    pconfig->Write(wxT("/Perferences/EnablePlugins"), enablePlug);
    pconfig->Flush();
}

bool kWookaIsEnableAutoStartup() {
    wxInt32  enableAutoStartup = 0;
    wxConfigBase* pconfig = wxConfigBase::Get();
    pconfig->Read(wxT("/Perferences/EnableAutoStartup"), &enableAutoStartup);
    return enableAutoStartup != 0;
}

void kWookaSetEnableAutoStartup(bool fs) {
    wxInt32  enableAutoStartup = fs ?  1 : 0;
    wxConfigBase* pconfig = wxConfigBase::Get();
    pconfig->Write(wxT("/Perferences/EnableAutoStartup"), enableAutoStartup);
    pconfig->Flush();
    
#ifdef WIN32
    kWookaWindowAddOrDeleteStartup(fs);
#endif
}

bool kWookaIsEnableAutoCheckVersion(){
    wxInt32  enableCheckVersion = 0;
    wxConfigBase* pconfig = wxConfigBase::Get();
    pconfig->Read(wxT("/Perferences/EnableCheckVersion"), &enableCheckVersion);
    return enableCheckVersion != 0;
}

void kWookaSetEnableAutoCheckVersion(bool fs) {
    wxInt32  enableCheckVersion = fs ?  1 : 0;
    wxConfigBase* pconfig = wxConfigBase::Get();
    pconfig->Write(wxT("/Perferences/EnableCheckVersion"), enableCheckVersion);
    pconfig->Flush();
}


long  kWookaConvertToInteger(const wxString& stp) {
    double dbl = 0.0;
    if (stp.ToDouble(&dbl)) {
        int lng = (int) round(dbl * 100);
        return lng;
    }
    
    return 0;
}

wxString kWookaFormatDecimal(long pc, int p) {
    int fd = pc / p;
    int yd = pc % p;
    return wxString::Format(_T("%d.%02d"), fd, yd);
}


wxArrayString GetJavaHomeList() {
    wxArrayString jdks;
    wxRegKey reg(wxRegKey::HKLM, "SOFTWARE\\JavaSoft\\Java Development Kit");
    reg.Open(wxRegKey::Read);
    wxString key;
    long keyidx = 0; 
    bool found = reg.GetFirstKey(key, keyidx);
    while (found) {
        wxRegKey keyreg(reg, key);
        if (keyreg.Open(wxRegKey::Read)) {
            wxString keyvalue;
            keyreg.QueryRawValue(wxT("JavaHome"), keyvalue);
            if (jdks.Index(keyvalue) < 0) {
                jdks.Add(keyvalue);
            }
        }
        found = reg.GetNextKey(key, keyidx);
    }
    reg.Close();
    wxRegKey reg2(wxRegKey::HKLM, "SOFTWARE\\JavaSoft\\JDK");
    reg2.Open(wxRegKey::Read);
    found = reg2.GetFirstKey(key, keyidx);
    while (found) {
        wxRegKey keyreg(reg2, key);
        if (keyreg.Open(wxRegKey::Read)) {
            wxString keyvalue;
            keyreg.QueryRawValue(wxT("JavaHome"), keyvalue);
            if (jdks.Index(keyvalue) < 0) {
                jdks.Add(keyvalue);
            }
        }
        found = reg2.GetNextKey(key, keyidx);
    }
    reg.Close();
    return jdks;
}



BOOL GetProcessCmdLineByHandle(HANDLE hProcess, LPWSTR wBuf, DWORD dwBufLen, LPDWORD dwBuffActSize) {
    LONG                      status;
    PROCESS_BASIC_INFORMATION pbi;
    PEB                       Peb;
    RTL_USER_PROCESS_PARAMETERS        ProcParam;
    SIZE_T                    dwDummy;
    SIZE_T                    dwSize;
    LPVOID                    lpAddress;
    BOOL                      bRet = FALSE;

    // Retrieve information
    status = NtQueryInformationProcess(hProcess,
        ProcessBasicInformation,
        (PVOID)&pbi,
        sizeof(PROCESS_BASIC_INFORMATION),
        NULL
    );

    if (status)
        goto cleanup;

    if (!ReadProcessMemory(hProcess,
        pbi.PebBaseAddress,
        &Peb,
        sizeof(PEB),
        &dwDummy
    )
        )
        goto cleanup;

    if (!ReadProcessMemory(hProcess,
        Peb.ProcessParameters,
        &ProcParam,
        sizeof(RTL_USER_PROCESS_PARAMETERS),
        &dwDummy
    )
        )
        goto cleanup;

    lpAddress = ProcParam.CommandLine.Buffer;
    dwSize = ProcParam.CommandLine.Length;

    if (dwBufLen < dwSize || wBuf == NULL) {
        if (dwBuffActSize != NULL) {
            *dwBuffActSize = dwSize;
            bRet = TRUE;
        }
        goto cleanup;
    }

    if (!ReadProcessMemory(hProcess,
        lpAddress,
        wBuf,
        dwSize,
        &dwDummy))
        goto cleanup;

    bRet = TRUE;

cleanup:
    return bRet;
}


BOOL GetProcessCmdLine(DWORD dwId, LPWSTR wBuf, DWORD dwBufLen, LPDWORD dwBuffActSize) {
    HANDLE                    hProcess;
    
    // Get process handle
    hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwId);
    if (!hProcess)
        return FALSE;

    BOOL bRet = GetProcessCmdLineByHandle(hProcess, wBuf, dwBufLen, dwBuffActSize);
    CloseHandle(hProcess);
    return bRet;
}


wxString GetProcessCmdLineByHandle(HANDLE hProcess) {
    DWORD dwBufLen = 0;
    wxString cmdline;
    if (GetProcessCmdLineByHandle(hProcess, NULL, dwBufLen, &dwBufLen)) {
        LPWSTR wBuf = (LPWSTR)malloc(sizeof(wchar_t) * dwBufLen);
        if (wBuf) {
            memset(wBuf, 0, sizeof(wchar_t) * dwBufLen);
            if (GetProcessCmdLineByHandle(hProcess, wBuf, dwBufLen, NULL)) {
                cmdline.Append(wBuf);
            }
            free(wBuf);
        }
    }
    return cmdline;
}


wxString GetProcessCmdLine(DWORD dwId) {
    DWORD dwBufLen = 0;
    wxString cmdline;
    if (GetProcessCmdLine(dwId, NULL, dwBufLen, &dwBufLen)) {
        LPWSTR wBuf = (LPWSTR)malloc(sizeof(wchar_t) * dwBufLen);
        if (wBuf) {
            memset(wBuf, 0, sizeof(wchar_t) * dwBufLen);
            if (GetProcessCmdLine(dwId, wBuf, dwBufLen, NULL)) {
                cmdline.Append(wBuf);
            }
            free(wBuf);
        }
    }
    return cmdline;
}


wxVector<DWORD> kWookaGetProcessListByCmdline(const wxString& cmdline)
{
    wxVector<DWORD> processes;
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (INVALID_HANDLE_VALUE == hSnapshot)
    {
        return processes;
    }
    PROCESSENTRY32 pe = { 0 };
    pe.dwSize = sizeof(PROCESSENTRY32);

    BOOL fOk;
    for (fOk = Process32First(hSnapshot, &pe); fOk; fOk = Process32Next(hSnapshot, &pe))
    {
        wxString cmd = GetProcessCmdLine(pe.th32ProcessID);
        if (cmd == cmdline) {
            processes.push_back(pe.th32ProcessID);
        }
    }
    CloseHandle(hSnapshot);
    return processes;
}


wxString* wxArrayStringJoin(wxArrayString& lines, const wxString& joinable) {
    size_t rs = 0;
    size_t jbs = joinable.length();
    for (size_t s = 0; s < lines.Count(); s++) {
        rs += lines[s].length() + jbs;
    }
    wchar_t* buffer = (wchar_t*)malloc(sizeof(wchar_t) * (rs + 2));
    if (buffer != NULL) {
        size_t trs = 0;
        size_t bs = sizeof(wchar_t) * jbs;
        memset(buffer, 0, sizeof(wchar_t) * (rs + 2));
        for (size_t s = 0; s < lines.Count(); s++) {
            // size_t ts = lines[s].length() * sizeof(wchar_t);
            const wchar_t* wcstr = lines[s].wc_str();
            size_t ts = wcslen(wcstr) * sizeof(wchar_t);
            memcpy_s((char*)buffer + trs, ts, wcstr, ts);
            memcpy_s((char*)buffer + trs + ts, bs, joinable.wc_str(), bs);
            trs += ts + bs;
        }
        wxString* pstr = new wxString(buffer, rs);
        free(buffer);
        return pstr;
    }
    return NULL;
}